const co = require('co');
const Eos = require('eosjs');
const fetch = require('node-fetch');
const logger = require('../utils/logger');

const creator = process.env.EOS_ACCOUNT_NAME;

const eos = Eos({
    httpEndpoint: process.env.PRODUCTION_ENDPOINT,
    chainId: 'aca376f206b8fc25a6ed44dbdc66547c36c6c33e3a119ffbeaef943642f0e906',
    keyProvider: [
        process.env.EOS_PRIVATE_KEY,
    ],
    verbose: false,
    logger: { log: null, error: null }
});

const testEos = Eos({
    httpEndpoint: process.env.TESTING_ENDPOINT,
    keyProvider: [
        process.env.EOS_PRIVATE_KEY
    ],
    verbose: false,
    logger: { log: null, error: null }
});

function getEos() {
    if (process.env.NODE_ENV === 'production') {
        return eos;
    } else {
        return testEos;
    }
}

const costOfTransfer = {
    net: 137, // bytes
    cpu: 1000, // ns
};

const costOfCreateAccount = {
    ram: 162, // bytes
    net: 326, // bytes
    cpu: 10130 // ns
};

const availableRamRate = 0.994;
// const baseRam = 2996; //bytes, ~ 2.926Kb
const baseRam = 3396; //bytes, ~ 2.926Kb

function getNewAccountQuota () {
    return {
        netStake: 0.01,
        cpuStake: 0.2,
        ram: Math.ceil((baseRam + 30) / availableRamRate) + 30
    };
}

function parseAsset (asset) {
    return asset.split(' ')[0];
}

let estimateFee = co.wrap(function *() {
    // Calculate net & cpu price
    let accInfo = yield eos.getAccount(creator);
    logger.info('Account Info:', JSON.stringify(accInfo));

    let priceOfNet = parseAsset(accInfo.total_resources.net_weight) / accInfo.net_limit.max / 3;
    let priceOfCpu = parseAsset(accInfo.total_resources.cpu_weight) / accInfo.cpu_limit.max / 3;
    logger.info('Price of Net (per byte per day):', priceOfNet);
    logger.info('Price of Cpu (per us per day):', priceOfCpu);

    // Calculate ram price
    let ramInfo = yield eos.getTableRows({
        json: true,
        code: 'eosio',
        scope: 'eosio',
        table: 'rammarket'
    });
    logger.info('Ram Info:', JSON.stringify(ramInfo));
    ramInfo = ramInfo.rows[0];
    let priceOfRam = parseAsset(ramInfo.quote.balance) / parseAsset(ramInfo.base.balance);
    logger.info('Price of Ram (per byte):', priceOfRam);

    // Get EOS price
    let eosResp = yield fetch('https://api.coinmarketcap.com/v1/ticker/eos/');
    let eosInfo = yield eosResp.json();
    logger.info('EOS Info:', JSON.stringify(eosInfo));
    let usdOfEOS = eosInfo[0].price_usd;
    logger.info("Price of EOS (USD):", usdOfEOS);

    // USD_CNY convertion
    let convResp = yield fetch('http://free.currencyconverterapi.com/api/v5/convert?q=USD_CNY&compact=y');
    let convInfo = yield convResp.json();
    logger.info('Convert Info:', JSON.stringify(convInfo));
    let usdToCny = convInfo.USD_CNY.val;
    let cnyOfEOS = usdOfEOS * usdToCny;
    logger.info("Price of EOS (CNY):", cnyOfEOS);

    // Estimate fee
    // let buyNet = costOfTransfer.net * 30; // bytes
    // let buyCpu = costOfTransfer.cpu * 30; // ns
    // let buyRam = Math.ceil((baseRam + 30) / availableRamRate); // bytes
    // let netCost = buyNet * priceOfNet;
    // let cpuCost = buyCpu * priceOfCpu;

    let accountQuota = getNewAccountQuota();
    let netCost = accountQuota.netStake;
    let cpuCost = accountQuota.cpuStake;
    let ramCost = (accountQuota.ram + costOfCreateAccount.ram) * priceOfRam;
    logger.info(netCost, cpuCost, ramCost);

    let totalCost = netCost + cpuCost + ramCost;
    let totalCostCNY = totalCost * cnyOfEOS;
    logger.info('Fee (EOS):', totalCost);
    logger.info('Fee (CNY):', totalCostCNY);

    let chargeCNY = Math.ceil(totalCostCNY * 1.2);
    if (process.env.MIN_CHARGE) {
        let minCharge = parseFloat(process.env.MIN_CHARGE);
        if (chargeCNY < minCharge) {
            chargeCNY = minCharge;
        }
    }

    if (process.env.NODE_ENV !== 'production') {
        chargeCNY = parseFloat(process.env.FAKE_CHARGE) || chargeCNY;
    }

    // 1.2 既包含了手續費(20%)
    return {
        totalCost: parseFloat((totalCost * 1.2).toFixed(4)), // 取小数点后四位
        totalCostCNY: chargeCNY
    };
});

let createAccount = co.wrap(function *(params) {
    logger.info('Create Account with Parameters:', params);

    var accData = {
        creator: creator,
        name: params.accountName,
        owner: params.publicKey,
        active: params.publicKey
    };

    yield getEos().transaction(tr => {
        tr.newaccount(accData);

        tr.buyrambytes({
            payer: creator,
            receiver: params.accountName,
            bytes: params.ram
        });

        tr.delegatebw({
            from: creator,
            receiver: params.accountName,
            stake_net_quantity: `${params.netStake.toFixed(4)} EOS`,
            stake_cpu_quantity: `${params.cpuStake.toFixed(4)} EOS`,
            transfer: 0
        });
    });

    logger.info('Account created:', accData);
});

let isAccountExist = co.wrap(function *(accountName) {
    try {
        let accountInfo = yield getEos().getAccount(accountName);
        if (accountInfo) {
            return true;
        } else {
            return false;
        }
    } catch (e) {
        return false;
    }
});

// 进行eos转账
const transferEos = async (transferFrom, transferTo, transferBalance, memo) => {
    console.log("transferFrom", transferFrom)
    console.log("transferTo", transferTo)
    console.log("transferBalance", transferBalance)
    console.log("memo", memo)

    try {
        await getEos().transfer(
            transferFrom,
            transferTo,
            transferBalance,
            memo,
            {
                authorization: transferFrom,
                broradcast: true,
                sign: true,
            },
        );
    } catch (err) {
        logger.error("transferEos", err.message)
    }
}

module.exports = {
    estimateFee,
    createAccount,
    getNewAccountQuota,
    isAccountExist,
    transferEos
}